package com.gitee.wsl.compose.chart.geometry.model

import kotlin.math.sqrt

/*
 * Copyright 2013 Hannes Janetzek
 *
 * This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
 *
 * This program is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * ported from http://www.flipcode.com/archives/2D_OBB_Intersection.shtml
 */
class OBB2D {
    /**
     * Vector math for one array
     */
    object Vec2 {
        operator fun set(v: FloatArray, pos: Int, x: Float, y: Float) {
            v[pos + 0] = x
            v[pos + 1] = y
        }

        fun dot(v: FloatArray, a: Int, b: Int): Float {
            return v[a] * v[b] + v[a + 1] * v[b + 1]
        }

        fun lengthSquared(v: FloatArray, pos: Int): Float {
            val x = v[pos + 0]
            val y = v[pos + 1]
            return x * x + y * y
        }

        fun normalizeSquared(v: FloatArray, pos: Int) {
            val x = v[pos + 0]
            val y = v[pos + 1]
            val length = x * x + y * y
            v[pos + 0] = x / length
            v[pos + 1] = y / length
        }

        fun normalize(v: FloatArray, pos: Int) {
            val x = v[pos + 0]
            val y = v[pos + 1]
            val length = sqrt((x * x + y * y).toDouble())
            v[pos + 0] = (x / length).toFloat()
            v[pos + 1] = (y / length).toFloat()
        }

        fun length(v: FloatArray, pos: Int): Float {
            val x = v[pos + 0]
            val y = v[pos + 1]
            return sqrt((x * x + y * y).toDouble()).toFloat()
        }

        fun add(v: FloatArray, r: Int, a: Int, b: Int) {
            v[r + 0] = v[a + 0] + v[b + 0]
            v[r + 1] = v[a + 1] + v[b + 1]
        }

        fun sub(v: FloatArray, r: Int, a: Int, b: Int) {
            v[r + 0] = v[a + 0] - v[b + 0]
            v[r + 1] = v[a + 1] - v[b + 1]
        }

        fun mul(v: FloatArray, pos: Int, a: Float) {
            v[pos + 0] *= a
            v[pos + 1] *= a
        }
    }

    var originX = 0f
    var originY = 0f
    val vec = FloatArray(4 * 2 + 2 * 2)

    // Returns true if other overlaps one dimension of this.
    private fun overlaps1Way(other: OBB2D): Boolean {
        var a = 0
        while (a <= 2) {
            val ax = vec[AXIS_X + a]
            val ay = vec[AXIS_Y + a]

            // dot product
            var t = ax * other.vec[CORNER_X] + ay * other.vec[CORNER_Y]

            // Find the extent of box 2 on axis a
            var tMin = t
            var tMax = t
            var c = CORNER_X + 2
            while (c < 8) {
                t = ax * other.vec[c] + ay * other.vec[c + 1]
                if (t < tMin) tMin = t else if (t > tMax) tMax = t
                c += 2
            }

            // We have to subtract off the origin
            // See if [tMin, tMax] intersects [0, 1]
            if (a == 0) {
                if (tMin > 1 + originX || tMax < originX) // There was no intersection along this dimension;
                // the boxes cannot possibly overlap.
                    return false
            } else {
                if (tMin > 1 + originY || tMax < originY) return false
            }
            a += 2
        }

        // There was no dimension along which there is no intersection.
        // Therefore the boxes overlap.
        return true
    }

    // Updates the axes after the corners move.  Assumes the
    // corners actually form a rectangle.
    private fun computeAxes() {
        Vec2.sub(vec, AXIS_1, CORNER_1, CORNER_0)
        Vec2.sub(vec, AXIS_2, CORNER_3, CORNER_0)

        // Make the length of each axis 1/edge length so we know any
        // dot product must be less than 1 to fall within the edge.
        Vec2.normalizeSquared(vec, AXIS_1)
        originX = Vec2.dot(vec, CORNER_0, AXIS_1)
        Vec2.normalizeSquared(vec, AXIS_2)
        originY = Vec2.dot(vec, CORNER_0, AXIS_2)
    }

    //	public OBB2D(float cx, float cy, float w, float h, float angle) {
    //		float rcos = (float) Math.cos(angle);
    //		float rsin = (float) Math.sin(angle);
    //
    //		float[] tmp = new float[4 * 2];
    //		Vec2.set(tmp, 0, rcos, rsin);
    //		Vec2.set(tmp, 1, -rsin, rcos);
    //
    //		Vec2.mul(tmp, 0, w / 2);
    //		Vec2.mul(tmp, 1, h / 2);
    //
    //		Vec2.add(tmp, 2, tmp, 0, tmp, 1);
    //		Vec2.sub(tmp, 3, tmp, 0, tmp, 1);
    //
    //		Vec2.set(tmp, 0, cx, cy);
    //
    //		Vec2.sub(origin, CORNER_X + 0, tmp, 0, tmp, 3);
    //		Vec2.add(origin, CORNER_X + 2, tmp, 0, tmp, 3);
    //		Vec2.add(origin, CORNER_X + 4, tmp, 0, tmp, 2);
    //		Vec2.sub(origin, CORNER_X + 6, tmp, 0, tmp, 2);
    //
    //		computeAxes();
    //	}
    //
    constructor()
    constructor(cx: Float, cy: Float, width: Float, height: Float, acos: Double, asin: Double) {
        val vx = acos.toFloat() * width / 2
        val vy = asin.toFloat() * width / 2
        val ux = -asin.toFloat() * height / 2
        val uy = acos.toFloat() * height / 2
        vec[CORNER_X] = cx + (vx - ux)
        vec[CORNER_Y] = cy + (vy - uy)
        vec[CORNER_X + 2] = cx + (-vx - ux)
        vec[CORNER_Y + 2] = cy + (-vy - uy)
        vec[CORNER_X + 4] = cx + (-vx + ux)
        vec[CORNER_Y + 4] = cy + (-vy + uy)
        vec[CORNER_X + 6] = cx + (vx + ux)
        vec[CORNER_Y + 6] = cy + (vy + uy)
        computeAxes()
    }

    fun setNormalized(
        cx: Float, cy: Float, vx: Float, vy: Float, width: Float, height: Float,
        dy: Float
    ) {
        var cx = cx
        var cy = cy
        var vx = vx
        var vy = vy
        var ux = -vy
        var uy = vx
        val hw = width / 2
        val hh = height / 2
        if (dy != 0f) {
            cx += vx * dy + vy * dy
            cy += -vy * dy + vx * dy
        }
        vx *= hw
        vy *= hw
        ux *= hh
        uy *= hh
        vec[CORNER_X] = cx - (vx - ux)
        vec[CORNER_Y] = cy - (vy - uy)
        vec[CORNER_X + 2] = cx + (vx - ux)
        vec[CORNER_Y + 2] = cy + (vy - uy)
        vec[CORNER_X + 4] = cx + (vx + ux)
        vec[CORNER_Y + 4] = cy + (vy + uy)
        vec[CORNER_X + 6] = cx - (vx + ux)
        vec[CORNER_Y + 6] = cy - (vy + uy)
        computeAxes()
    }

    operator fun set(cx: Float, cy: Float, dx: Float, dy: Float, width: Float, height: Float) {
        var vx = cx - dx
        var vy = cy - dy
        val a = sqrt((vx * vx + vy * vy).toDouble()).toFloat()
        vx /= a
        vy /= a
        val hw = width / 2
        val hh = height / 2
        val ux = vy * hh
        val uy = -vx * hh
        vx *= hw
        vy *= hw
        vec[CORNER_X] = cx - vx - ux
        vec[CORNER_Y] = cy - vy - uy
        vec[CORNER_X + 2] = cx + vx - ux
        vec[CORNER_Y + 2] = cy + vy - uy
        vec[CORNER_X + 4] = cx + vx + ux
        vec[CORNER_Y + 4] = cy + vy + uy
        vec[CORNER_X + 6] = cx - vx + ux
        vec[CORNER_Y + 6] = cy - vy + uy
        computeAxes()
    }

    constructor(cx: Float, cy: Float, dx: Float, dy: Float, width: Float, height: Float) {
        var vx = cx - dx
        var vy = cy - dy
        val a = sqrt((vx * vx + vy * vy).toDouble()).toFloat()
        vx /= a
        vy /= a
        val hw = width / 2
        val hh = height / 2
        val ux = vy * hh
        val uy = -vx * hh
        vx *= hw
        vy *= hw
        vec[CORNER_X + 0] = cx - vx - ux
        vec[CORNER_Y + 0] = cy - vy - uy
        vec[CORNER_X + 2] = cx + vx - ux
        vec[CORNER_Y + 2] = cy + vy - uy
        vec[CORNER_X + 4] = cx + vx + ux
        vec[CORNER_Y + 4] = cy + vy + uy
        vec[CORNER_X + 6] = cx - vx + ux
        vec[CORNER_Y + 6] = cy - vy + uy
        computeAxes()
    }

    // width and height must be > 1 I guess
    constructor(cx: Float, cy: Float, width: Float, height: Float) {
        val hw = width / 2
        val hh = height / 2
        vec[CORNER_X] = cx - hw
        vec[CORNER_Y] = cy - hh
        vec[CORNER_X + 2] = cx - hw
        vec[CORNER_Y + 2] = cy + hh
        vec[CORNER_X + 4] = cx + hw
        vec[CORNER_Y + 4] = cy + hh
        vec[CORNER_X + 6] = cx + hw
        vec[CORNER_Y + 6] = cy - hh
        vec[AXIS_X + 0] = 0f
        vec[AXIS_X + 1] = 1 / height
        vec[AXIS_X + 2] = 1 / width
        vec[AXIS_X + 3] = 0f
        vec[0] = vec[CORNER_Y] * vec[AXIS_Y]
        vec[1] = vec[CORNER_X + 2] * vec[AXIS_X + 2]
    }

    // Returns true if the intersection of the boxes is non-empty.
    fun overlaps(other: OBB2D): Boolean {
        return overlaps1Way(other) && other.overlaps1Way(this)
    }

    companion object {
        // Corners of the box, where 0 is the lower left.
        //public final float[] corner = new float[ 4 * 2];
        private const val CORNER_X = 0
        private const val CORNER_Y = CORNER_X + 1
        private const val CORNER_0 = CORNER_X
        private const val CORNER_1 = CORNER_X + 2

        //private final static int CORNER_2 = CORNER_X + 4;
        private const val CORNER_3 = CORNER_X + 6

        // Two edges of the box extended away from origin[CORNER_X + 0].
        //public final float[] axis = new float[2 * 2];
        private const val AXIS_X = 2 * 4
        private const val AXIS_Y = AXIS_X + 1
        private const val AXIS_1 = AXIS_X
        private const val AXIS_2 = AXIS_X + 2
    }
}

